# -*- coding: utf-8 -*-
import os
import asyncio
import brotli
import struct
import json
import requests
from aiowebsocket.converses import AioWebSocket
from PySide6.QtCore import *


class getGiftLink(QThread):
    info = Signal(dict)

    def __init__(self, roomID):
        super(getGiftLink, self).__init__()
        self.roomID = roomID

    def run(self):
        giftInfo = {}
        html = requests.get(r'https://api.live.bilibili.com/xlive/web-room/v1/giftPanel/giftConfig?platform=pc&room_id=%s' % self.roomID)
        gifts = json.loads(html.text)['data']['list']
        for gift in gifts:
            name, price, coin, link = gift['name'], gift['price'], gift['coin_type'], gift['img_basic']
            name = name.strip()
            if not os.path.exists('gift/%s.png' % name):
                if coin == 'silver':
                    giftInfo[name] = [40, link]
                elif price <= 10000:
                    giftInfo[name] = [80, link]
                elif price <= 100000:
                    giftInfo[name] = [120, link]
                else:
                    giftInfo[name] = [160, link]
        self.info.emit(giftInfo)


def unpack(data: bytes):
    """
    解包数据
    """
    ret = []
    offset = 0
    header = struct.unpack(">IHHII", data[:16])
    if header[2] == 3:
        realData = brotli.decompress(data[16:])
    else:
        realData = data

    if header[2] == 1 and header[3] == 3:
        realData = realData[16:]
        # 心跳包协议特殊处理
        recvData = {
            "protocol_version": header[2],
            "datapack_type": header[3],
            "data": {
                "view": struct.unpack('>I', realData[0:4])[0]
            }
        }
        ret.append(recvData)
        return ret

    while offset < len(realData):
        header = struct.unpack(">IHHII", realData[offset:offset + 16])
        length = header[0]
        recvData = {
            "protocol_version": header[2],
            "datapack_type": header[3],
            "data": None
        }
        chunkData = realData[(offset + 16):(offset + length)]
        if header[2] == 0:
            recvData["data"] = json.loads(chunkData.decode())
        elif header[2] == 2:
            recvData["data"] = json.loads(chunkData.decode())
        elif header[2] == 1:
            if header[3] == 3:
                recvData["data"] = {
                    "view": struct.unpack(">I", chunkData)[0]}
            elif header[3] == 8:
                recvData["data"] = json.loads(chunkData.decode())
        ret.append(recvData)
        offset += length
    return ret


class remoteThread(QThread):
    giftInfo = Signal(dict)

    def __init__(self, roomID):
        super(remoteThread, self).__init__()
        self.roomID = roomID
        self.giftLink = {}
        if len(self.roomID) <= 4:
            html = requests.get(r'https://api.live.bilibili.com/room/v1/Room/room_init?id=%s' % self.roomID).json()
            self.roomID = html['data']['room_id']
        self.getGiftLink = getGiftLink(self.roomID)
        self.getGiftLink.info.connect(self.updateGiftLink)
        self.getGiftLink.start()

    def updateGiftLink(self, info):
        self.giftLink = info

    async def startup(self, url):
        verifyData = {"roomid": int(self.roomID), "protover": 3}
        req = json.dumps(verifyData)
        head = bytearray(
            [0x00, 0x00, 0x00, 16 + len(req),
             0x00, 0x10, 0x00, 0x01,
             0x00, 0x00, 0x00, 0x07,
             0x00, 0x00, 0x00, 0x01]
        )
        data_raw = bytes(head + req.encode())
        async with AioWebSocket(url) as aws:
            converse = aws.manipulator
            await converse.send(data_raw)
            tasks = [self.receDM(converse), self.sendHeartBeat(converse)]
            await asyncio.wait(tasks)

    async def sendHeartBeat(self, websocket):
        hb = '00000010001000010000000200000001'
        while True:
            await asyncio.sleep(30)
            await websocket.send(bytes.fromhex(hb))

    async def receDM(self, websocket):
        while True:
            recv_text = await websocket.receive()
            self.printDM(recv_text)

    def printDM(self, data):
        if data:
            data = unpack(data)
            for info in data:
                if info['datapack_type'] == 5:  # 弹幕 礼物
                    jd = info["data"]
                    try:
                        if jd['cmd'] == 'SEND_GIFT':
                            d = jd['data']
                            self.giftInfo.emit(
                                {
                                    'key': '%s_%s' % (d['uname'], d['giftName']),
                                    'cmd': 'SEND_GIFT',
                                    'type': 'gift',
                                    'username': d['uname'],
                                    'giftname': d['giftName'],
                                    'num': d['num'],
                                    'price': 0 if d['coin_type'] == 'silver' else d['price'] * d['num'] / 1000,
                                    'link': self.giftLink.get(d['giftName'], []),
                                }
                            )
                        elif jd['cmd'] == 'COMBO_SEND':
                            d = jd['data']
                            self.giftInfo.emit(
                                {
                                    'key': '%s_%s' % (d['uname'], d['gift_name']),
                                    'cmd': 'COMBO_SEND',
                                    'type': 'gift',
                                    'username': d['uname'],
                                    'giftname': d['gift_name'],
                                    'num': d['batch_combo_num'],
                                    'price': d['combo_total_coin'] / 1000,
                                    'link': self.giftLink.get(d['gift_name'], []),
                                }
                            )
                        elif jd['cmd'] == 'GUARD_BUY':
                            d = jd['data']
                            self.giftInfo.emit(
                                {
                                    'key': '%s_%s' % (d['username'], d['gift_name']),
                                    'cmd': 'GUARD_BUY',
                                    'type': 'guard',
                                    'username': d['username'],
                                    'giftname': d['gift_name'],
                                    'num': d['num'],
                                    'price': d['price'] / 1000
                                }
                            )
                        elif jd['cmd'] == 'SUPER_CHAT_MESSAGE':
                            d = jd['data']
                            self.giftInfo.emit(
                                {
                                    'key': '%s_%s' % (d['user_info']['uname'], d['message']),
                                    'cmd': 'SUPER_CHAT_MESSAGE',
                                    'type': 'sc',
                                    'username': d['user_info']['uname'],
                                    'giftname': d['message'],
                                    'num': 1,
                                    'price': d['price']
                                }
                            )
                    except Exception as e:
                        print(e)

    def run(self):
        remote = r'wss://broadcastlv.chat.bilibili.com:2245/sub'
        try:
            asyncio.set_event_loop(asyncio.new_event_loop())
            asyncio.get_event_loop().run_until_complete(self.startup(remote))
        except KeyboardInterrupt as e:
            print('exit')